﻿using System;
using NLite.Messaging.Internal;
using System.Runtime.Serialization;
using NLite.Internal;
using System.Collections.Generic;
using NLite.Threading;
using System.Threading;

namespace NLite.Messaging
{
    /// <summary>
    /// 消息接口
    /// </summary>
    public interface IMessage: IMessageResult,IDisposable
    {
        /// <summary>
        /// 消息名称/消息主题
        /// </summary>
        string Name { get; }

        /// <summary>
        /// 消息数据
        /// </summary>
        object Data { get; }

       

        /// <summary>
        /// 消息创建事件
        /// </summary>
        DateTime Timestamp { get; }

        /// <summary>
        /// <c>true</c> when the event was handled; otherwise, <c>false</c>.
        /// </summary>
        /// <remarks><c>false</c> does not mean that the handling failed, but that the 
        /// handler was not interested in handling this event.</remarks></remarks>
        bool IsHandled { get; }

        /// <summary>
        /// 得到一个布尔值用来指示是否处理完毕
        /// </summary>
        bool IsCompleted { get; }

        /// <summary>
        /// 得到或设置取消消息调用
        /// </summary>
        bool Canceled { get; set; }

        bool Wait();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    public interface IMessage<TData> : IMessage
    {
        /// <summary>
        /// 
        /// </summary>
        new TData Data { get; }
    }

    /// <summary>
    /// 
    /// </summary>
    public interface ICallback:IDisposable
    {
        /// <summary>
        /// 
        /// </summary>
        object[] Args { get; }
        /// <summary>
        /// 
        /// </summary>
        Delegate Delegate { get; }
    }

    /// <summary>
    /// 消息执行的结果列表
    /// </summary>
    public interface IMessageResult
    {
        /// <summary>
        /// 得到消息执行的结果列表
        /// </summary>
        object[] Results { get; }
    }

    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class Callback:ICallback
    {
        /// <summary>
        /// 
        /// </summary>
        public Delegate Delegate { get; private set; }
        /// <summary>
        /// 
        /// </summary>
        public object[] Args { get; private set; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="args"></param>
        public Callback(Delegate callback, params object[] args)
        {
            Delegate = callback;
            Args = args;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            Delegate = null;
        }
    }
    

    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    [DataContract]
    public class Message : BooleanDisposable, IMessage
    {
        private bool isCompleted;
        private ResetEvent resetEvent;
        private Action<Message> completedHandler = m => { };
        private bool canceled = false;

        /// <summary>
        /// 
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        object IMessage.Data { get { return _data; } }

        /// <summary>
        /// 
        /// </summary>
        public DateTime Timestamp { get; private set; }

        /// <summary>
        /// <c>true</c> when the event was handled; otherwise, <c>false</c>.
        /// </summary>
        /// <remarks><c>false</c> does not mean that the handling failed, but that the 
        /// handler was not interested in handling this event.</remarks></remarks>
        public bool IsHandled { get; internal set; }

        /// <summary>
        /// 得到一个布尔值用来指示是否处理完毕
        /// </summary>
        public bool IsCompleted
        {
            get { return isCompleted; }
            internal set
            {
                if (isCompleted != value)
                {
                    isCompleted = value;
                    if (isCompleted)
                    {
                        if(resetEvent != null)
                            resetEvent.Set();
                        resetEvent = null;
                        var handler = completedHandler;
                        if (handler != null)
                            handler(this);

                        handler = null;
                        if (typeof(ICallback).IsInstanceOfType(_data))
                        {
                            var callback = (ICallback)this._data;
                            if (callback != null && callback.Delegate != null)
                            {
                                callback.Dispose();
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Action<Message> CompletedHandler
        {
            get { return completedHandler; }
            set
            {
                CheckNotDisposed();
                completedHandler = value;
            }
        }

        

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool Wait()
        {
            return Wait(Timeout.Infinite);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public bool Wait(int timeout)
        {
            CheckNotDisposed();

            if (IsCompleted) return true;
            if (resetEvent == null)
                resetEvent = new ResetEvent(false);
            return resetEvent.Wait(timeout);
        }

        internal List<object> results = new List<object>();

        /// <summary>
        /// 得到消息执行的结果列表
        /// </summary>
        public object[] Results
        {
            get
            {
                CheckNotDisposed();
                return results.ToArray();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool Canceled
        {
            get { return canceled; }
            set
            {
                CheckNotDisposed();
                if (canceled != value)
                {
                    canceled = value;
                }
            }
        }

        internal readonly object _data;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <param name="timestamp"></param>
        public Message(string name, object data,DateTime timestamp)
        {
            //Assumes.NotNullOrEmpty(name);
            Name = name;
            _data = data;
            Timestamp = timestamp;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        public Message(string name, object data):this(name,data,DateTime.Now)
        {
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        public Message(string name):this(name,null,DateTime.Now)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            CheckNotDisposed();
            return _data != null ? _data.ToString() :null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (!isCompleted)
                {
                    resetEvent.Wait();
                }
            }
        }


    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    [Serializable]
    [DataContract]
    public class Message<TData> : Message, IMessage<TData>
    {
        /// <summary>
        /// 
        /// </summary>
        public TData Data { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        public Message(string name, TData data) : base(name, data,DateTime.Now) { }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <param name="timestamp"></param>
        public Message(string name, TData data,DateTime timestamp) : base(name, data,timestamp) { }
    }

    /// <summary>
    /// 
    /// </summary>
    public sealed class MessageFactory
    {
        static readonly Type MessageType = typeof(IMessage);

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TData"></typeparam>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static IMessage Make<TData>(string name, TData data)
        {
            return MessageFactory.MessageType.IsAssignableFrom(data.GetType()) ? (IMessage)data : new Message<TData>(name, data);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static IMessage Make(string name, object data)
        {
            return MessageFactory.MessageType.IsAssignableFrom(data.GetType()) ?(IMessage)data : new Message(name, data);
        }
        
    }
}
